{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Creating a basic chat experience with context variables\n",
    "\n",
    "In this example, we show how you can build a simple chat bot by sending and updating context with your requests. \n",
    "\n",
    "We introduce the Context Variables object which in this demo functions similarly as a key-value store that you can use when running the kernel.\n",
    "\n",
    "The context is local (i.e. in your computer's RAM) and not persisted anywhere beyond the life of this Jupyter session.\n",
    "\n",
    "In future examples, we will show how to persist the context on disk so that you can bring it into your applications.  \n",
    "\n",
    "In this chat scenario, as the user talks back and forth with the bot, the context gets populated with the history of the conversation. During each new run of the kernel, the context can provide the AI with its variables' content. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [],
   "source": [
    "#r \"nuget: Microsoft.SemanticKernel, 1.0.0-beta6\"\n",
    "#!import config/Settings.cs\n",
    "\n",
    "using Microsoft.SemanticKernel;\n",
    "using Microsoft.SemanticKernel.TemplateEngine;\n",
    "using Microsoft.SemanticKernel.Orchestration;\n",
    "using Microsoft.SemanticKernel.Connectors.AI.OpenAI;\n",
    "\n",
    "var builder = new KernelBuilder();\n",
    "\n",
    "// Configure AI backend used by the kernel\n",
    "var (useAzureOpenAI, model, azureEndpoint, apiKey, orgId) = Settings.LoadFromFile();\n",
    "if (useAzureOpenAI)\n",
    "    builder.WithAzureOpenAIChatCompletionService(model, azureEndpoint, apiKey);\n",
    "else\n",
    "    builder.WithOpenAIChatCompletionService(model, apiKey, orgId);\n",
    "\n",
    "IKernel kernel = builder.Build();"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's define a prompt outlining a dialogue chat bot."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [],
   "source": [
    "const string skPrompt = @\"\n",
    "ChatBot can have a conversation with you about any topic.\n",
    "It can give explicit instructions or say 'I don't know' if it does not have an answer.\n",
    "\n",
    "{{$history}}\n",
    "User: {{$userInput}}\n",
    "ChatBot:\";\n",
    "\n",
    "var aiRequestSettings = new OpenAIRequestSettings \n",
    "{\n",
    "    MaxTokens = 2000,\n",
    "    Temperature = 0.7,\n",
    "    TopP = 0.5\n",
    "};"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Register your semantic function"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [],
   "source": [
    "var chatFunction = kernel.CreateSemanticFunction(skPrompt, requestSettings: aiRequestSettings);"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Initialize your context"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [],
   "source": [
    "var context = kernel.CreateNewContext();\n",
    "\n",
    "var history = \"\";\n",
    "context.Variables[\"history\"] = history;"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Chat with the Bot"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [],
   "source": [
    "var userInput = \"Hi, I'm looking for book suggestions\";\n",
    "context.Variables[\"userInput\"] = userInput;\n",
    "\n",
    "var bot_answer = await chatFunction.InvokeAsync(context);"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Update the history with the output and set this as the new input value for the next request"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [],
   "source": [
    "history += $\"\\nUser: {userInput}\\nMelody: {bot_answer.GetValue<string>()}\\n\";\n",
    "context.Variables.Update(history);\n",
    "\n",
    "Console.WriteLine(context);"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Keep Chatting!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [],
   "source": [
    "Func<string, Task> Chat = async (string input) => {\n",
    "    // Save new message in the context variables\n",
    "    context.Variables[\"userInput\"] = input;\n",
    "\n",
    "    // Process the user message and get an answer\n",
    "    var answer = await chatFunction.InvokeAsync(context);\n",
    "\n",
    "    // Append the new interaction to the chat history\n",
    "    history += $\"\\nUser: {input}\\nMelody: {answer.GetValue<string>()}\\n\"; \n",
    "    context.Variables[\"history\"] = history;\n",
    "    \n",
    "    // Show the response\n",
    "    Console.WriteLine(context);\n",
    "};"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [],
   "source": [
    "await Chat(\"I would like a non-fiction book suggestion about Greece history. Please only list one book.\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [],
   "source": [
    "await Chat(\"that sounds interesting, what are some of the topics I will learn about?\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [],
   "source": [
    "await Chat(\"Which topic from the ones you listed do you think most people find interesting?\");"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [],
   "source": [
    "await Chat(\"could you list some more books I could read about the topic(s) you mentioned?\");"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "After chatting for a while, we have built a growing history, which we are attaching to each prompt and which contains the full conversation. Let's take a look!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "csharp"
    },
    "polyglot_notebook": {
     "kernelName": "csharp"
    },
    "vscode": {
     "languageId": "polyglot-notebook"
    }
   },
   "outputs": [],
   "source": [
    "Console.WriteLine(context.Variables[\"history\"]);"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".NET (C#)",
   "language": "C#",
   "name": ".net-csharp"
  },
  "language_info": {
   "file_extension": ".cs",
   "mimetype": "text/x-csharp",
   "name": "C#",
   "pygments_lexer": "csharp",
   "version": "11.0"
  },
  "polyglot_notebook": {
   "kernelInfo": {
    "defaultKernelName": "csharp",
    "items": [
     {
      "aliases": [],
      "name": "csharp"
     }
    ]
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
